package cn.lizemin.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @author lzm
 * @version 1.0
 * @description: 用户服务访问接口
 * @date 2022/2/23 15:32
 */
@RestController
public class UserController {

    @Autowired  //服务注册与发现客户端对象，可以从注册中心中，根据服务的名称拿到提供服务的集群机器列表
    private DiscoveryClient discoveryClient;

    @Autowired //负载均衡客户端对象
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private  RestTemplate restTemplate;

    private static final Logger log = LoggerFactory.getLogger(UserController.class);

    /**
     * 用户服务调用订单服务
     * @return
     */
    @GetMapping("user")
    public String invokeDemo() {
        log.info("user demo....");

        /**
         * 1.自定义负载均衡
         */
        //调用订单服务 服务地址url: http://localhost:9999/order
        // 必须GET方式    接收返回值 String类型
//        RestTemplate restTemplate = new RestTemplate();
//        String orderResult = restTemplate.getForObject("http://" + randomHost() + "/order", String.class);
//        log.info("调用订单服务成功：{}", orderResult);
//        return "调用订单服务成功，结果为： " + orderResult;

        /**
         * 2.使用ribbon来实现自定义负载均衡，从consul注册中心中来拿到服务列表，通过 DiscoveryClient对象
         * 这里并没有实现负载均衡，具体的实现还是需要自己实现。。。
         * 和第一种方案相比，唯一的区别是使用了负载均衡
         * <p/>
         * 需要注意的是：ribbon只是一个负载均衡的算法集合，发送请求实现服务调用还是需要依赖RestTemplate
         */
        //2.使用ribbon组件+RestTemplate实现负载均衡调用          @LoadBalance
//        List<ServiceInstance> serviceInstances = discoveryClient.getInstances("ORDERS");
//        serviceInstances.forEach(serviceInstance -> {
//            log.info("服务主机: {} 服务端口:{} 服务地址:{}",serviceInstance.getHost(),serviceInstance.getPort(),serviceInstance.getUri());
//        });
//        String result = new RestTemplate().getForObject(serviceInstances.get(0).getUri() + "/order", String.class);
//        return  "ok，" + result;

        /**
         * 3.方案三：使用LoadBalanceClient对象实现负载均衡
         * loadBalancerClient默认采用轮询方案
         */
        //3.使用loadbalanceClient进行服务调用
//        ServiceInstance serviceInstance = loadBalancerClient.choose("ORDERS");//默认轮询
//        log.info("服务地址:{} 服务主机:{} 服务端口:{}",serviceInstance.getUri(),serviceInstance.getHost(),serviceInstance.getPort());
//        RestTemplate restTemplate = new RestTemplate();
//        String result = restTemplate.getForObject(serviceInstance.getUri() + "/order", String.class);
//        return  "ok，" + result;

        /**
         * 4.方案四： 在RestTemplate上使用注解，让该对象自动具有负载均衡的特点
         */
        //4.使用@LoadBalanced注解 作用:可以让对象具有ribbon负载均衡特性
        String result = restTemplate.getForObject("http://ORDERS/order",String.class);

        //string自己  转为对象
        return  "ok" + result;

    }

    /**
     * 自定义随机策略，用于实现订单服务调用的负载均衡，
     * 缺点：
     *  没有考虑服务宕机的情况，如果某台服务器宕机了，但是自己依然调用它的服务，结果造成程序错误
     */
    private String randomHost(){
        List<String> hosts = new ArrayList<>();
        hosts.add("localhost:9999");
        hosts.add("localhost:9990");
        //生成随机数 只能在0-hosts.size()
        int i = new Random().nextInt(hosts.size());
        return hosts.get(i);
    }

}
